﻿using FreeSql;
using MiniJob;
using Orleans.Configuration;

namespace Microsoft.AspNetCore.Builder;

public static class OrleansExtension
{
    public static WebApplicationBuilder UseMiniJobOrleans(this WebApplicationBuilder builder)
    {
        var dbProvider = Appsettings.DbProvider;
        var dbConn = Appsettings.DbConn;

        if (string.IsNullOrEmpty(dbProvider))
        {
            throw new ArgumentNullException(dbProvider);
        }
        if (string.IsNullOrEmpty(dbConn))
        {
            throw new ArgumentNullException(dbConn);
        }
        
        var dbType = FreeSqlExtension.ProviderToFreesqlDbType(dbProvider);
        if (!dbType.HasValue)
        {
            throw new ArgumentException(nameof(dbProvider), $"[{dbProvider}] is not a freesql supported provider.");
        }

        var invariant = DbTypeToAdoInvariant(dbType.Value);
        // sqlite 本地开发环境/单机模式
        if (dbType.Value == DataType.Sqlite)
        {
            builder.Host.UseOrleans(siloBuilder =>
            {
                siloBuilder.UseLocalhostClustering();
                siloBuilder.AddMemoryGrainStorageAsDefault();
                siloBuilder.UseInMemoryReminderService();

                siloBuilder.Services.Configure<SiloMessagingOptions>(options =>
                {
                    // Silo关闭时等待通知客户端超时时间，默认为5s
                    options.ClientGatewayShutdownNotificationTimeout = SiloMessagingOptions.DEFAULT_CLIENT_GW_NOTIFICATION_TIMEOUT;
                    options.WaitForMessageToBeQueuedForOutboundTime = SiloMessagingOptions.DEFAULT_WAIT_FOR_MESSAGE_TO_BE_QUEUED_FOR_OUTBOUND_TIME;
                });
            });
        }
        else
        {
            builder.Host.UseOrleans(siloBuilder =>
            {
                siloBuilder.UseAdoNetClustering(options =>
                {
                    options.Invariant = invariant;
                    options.ConnectionString = dbConn;
                });
                siloBuilder.AddAdoNetGrainStorageAsDefault(options =>
                {
                    options.Invariant = invariant;
                    options.ConnectionString = dbConn;
                });
                siloBuilder.UseAdoNetReminderService(options =>
                {
                    options.Invariant = invariant;
                    options.ConnectionString = dbConn;
                });
            });
        }
        
        return builder;
    }

    public static string? DbTypeToAdoInvariant(DataType dbType)
    {
        switch (dbType)
        {
            case DataType.Sqlite:
                return null;
            case DataType.MySql:
                return "MySql.Data.MySqlConnector";
            case DataType.SqlServer:
                return "Microsoft.Data.SqlClient";
            case DataType.PostgreSQL:
                return "Npgsql";
            case DataType.Oracle:
                return "Oracle.DataAccess.Client";
            default:
                break;
        }

        return null;
    }
}
